// -*- C++ -*-

// Copyright 2006-2007 Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
// or its licensors, as applicable.
//
// You may not use this file except under the terms of the accompanying license.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License. You may
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Project:
// File:
// Purpose:
// Responsible: kapry
// Reviewer:
// Primary Repository:
// Web Sites: www.iupr.org, www.dfki.de

#include "colib.h"
#include "classmap.h"
#include "charlib.h"
#include "classify-chars.h"
#include "feature-extractor.h"

using namespace ocropus;
using namespace colib;

struct LineCharacterClassifier : IAdaptClassifier {
    autodel<Classifier> classifier;
    ClassMap map;
    //autodel<ILineFeatureExtractor> extractor;
    autodel<FeatureExtractor> extractor;
    bool output_garbage;
    objlist<nustring> variants;
    floatarray costs;
    bool init;

    virtual void set(bytearray &in) {throw "NI";}

    virtual void set(bytearray &in,
                     int baseline, int xheight, int descender, int ascender) {
        variants.clear();
        costs.clear();
        floatarray features;
        //extractor->extract(features, in, baseline,xheight,descender,ascender);
        extractor->setLineInfo(baseline,xheight);
        extractor->appendFeatures(features,in,FeatureExtractor::BAYS);
        extractor->appendFeatures(features,in,FeatureExtractor::GRAD);
        extractor->appendFeatures(features,in,FeatureExtractor::INCL);
        extractor->appendFeatures(features,in,FeatureExtractor::IMAGE);
        extractor->appendFeatures(features,in,FeatureExtractor::SKEL);
        extractor->appendFeatures(features,in,FeatureExtractor::SKELPTS);
        extractor->appendFeatures(features,in,FeatureExtractor::RELSIZE);
        extractor->appendFeatures(features,in,FeatureExtractor::POS);
        floatarray result;
        classifier->score(result, features);
        for(int i = 0; i < result.length(); i++) {
            //printf("score for class %d (%s) is %d\n", i, map.get_text(i), result[i]);
            //if (output_garbage) {  // if not garbage; is this a kluge?
                variants.push().utf8Decode(map.get_text(i), -1);
                costs.push(-log(result[i]));
            //}
        }
    }

    virtual void cls(nustring &result, int i) {
        copy(result, variants[i]);
    }

    virtual float cost(int i) {
        return costs[i];
    }

    virtual int length() {
        ASSERT(variants.length() == costs.length());
        return variants.length();
    }

    virtual void load(FILE *stream) {
        map.load(stream);
        classifier->load(stream);
        init = true;
    }

    virtual void save(FILE *stream) {
        map.save(stream);
        classifier->save(stream);
    }

    virtual void train(ICharacterLibrary &charlib) {
        classifier->start_training();
        int ninput = -1;
        for(int i = 0; i < charlib.sectionsCount(); i++) {
            charlib.switchToSection(i);
            if(charlib.charactersCount() == 0) continue;
            for(int j = 0; j < charlib.charactersCount(); j++) {
                floatarray features;
                //extractor->extract(features, charlib.character(j).image(),
                //                   charlib.character(j).baseline(),
                //                   charlib.character(j).xHeight(),
                //                   charlib.character(j).descender(),
                //                   charlib.character(j).ascender());
                extractor->setLineInfo(charlib.character(j).baseline(),
                                       charlib.character(j).xHeight());
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::BAYS);
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::GRAD); 
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::INCL);
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::IMAGE);
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::SKEL);
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::SKELPTS);
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::RELSIZE);
                extractor->appendFeatures(features,charlib.character(j).image(),
                                          FeatureExtractor::POS);
                ninput = features.length();
                if(init) {
                    int cls = map.get_class_no_add(charlib.character(j).text());
                    if(cls!=-1) {
                        classifier->add(features, cls);
                    }
                }
                else {
                    int cls = map.get_class(charlib.character(j).text());
                    classifier->add(features, cls);
                }
            }
            printf("extracted features from section %d\n", i);
        }
        if(!init) {
            classifier->param("ninput",ninput);
            classifier->param("noutput", map.length());
        }
        classifier->start_classifying();
        init = true;
    }

    virtual const char *description() {
        return "character classifier";
    }

    LineCharacterClassifier(Classifier *c, bool garbage):
        //classifier(c), extractor(make_LineFeatureExtractor()),
        classifier(c), extractor(make_FeatureExtractor()),
        output_garbage(garbage) {
        init = false;
    }
};

namespace ocropus {
    IAdaptClassifier *make_AdaptClassifier(Classifier *c, bool garbage) {
        return new LineCharacterClassifier(c, garbage);
    }
}
